﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using DomainDriver.CommonUtilities.Reflectors;
using DomainDriver.DomainModeling.DataProviders;

namespace DomainDriver.DomainModeling.Queries
{
    public class EqualsQuery<T> : IParameterizedQuery<T>
    {
        private IDataProvider m_DataProvider;
        private string m_ParameterName;
        private object m_DesiredParameterValue;

        public EqualsQuery(string parameterName, object desiredParameterValue)
        {
            if (parameterName == null)
            { throw new ApplicationException(); }

            m_ParameterName = parameterName;
            m_DesiredParameterValue = desiredParameterValue;
        }

        public IList<string> Parameters
        {
            get
            {
                List<string> parameters = new List<string>();
                parameters.Add(m_ParameterName);
                return parameters;
            }
        }

        public IDictionary<string, object> ParameterValues
        {
            get
            {
                Dictionary<string, object> parameterValues = new Dictionary<string, object>();
                parameterValues.Add(m_ParameterName, m_DesiredParameterValue);
                return parameterValues;
            }
        }

        public void SetQueryParameter(string parameterName, object value)
        {
            if (!m_ParameterName.Equals(parameterName))
            { throw new ApplicationException(); }

            m_DesiredParameterValue = value;
        }

        public Func<T, bool> GetQueryPredicate(IDataProvider dataProvider)
        {
            m_DataProvider = dataProvider;
            return CheckEqualsQuery;
        }

        protected bool CheckEqualsQuery(T objectToCheck)
        {
            Type objectType = typeof(T);
            List<PropertyInfo> properties = ClassReflector.GetNestedPropertiesByName(objectType, m_ParameterName);

            if (properties == null)
            { throw new ApplicationException("The property name in the query is invalid."); }
            if (properties.Count < 1)
            { throw new ApplicationException("The property name in the query is invalid."); }

            object valueFromQuery = m_DesiredParameterValue;
            object valueToCheck = ClassReflector.GetNestedPropertyValue(objectToCheck, properties);

            if ((valueFromQuery == null) && (valueToCheck == null))
            { return true; }
            if ((valueFromQuery == null) ^ (valueToCheck == null))
            { return false; }

            Type typeOfValueFromQuery = valueFromQuery.GetType();
            Type typeOfValueToCheck = valueToCheck.GetType();

            if (!typeOfValueToCheck.Equals(typeOfValueFromQuery))
            { return false; }
            if (!valueToCheck.Equals(valueFromQuery))
            { return false; }

            return true;
        }
    }
}